package com.polaris.live.ui.live.proxy.gift

import android.content.Context
import android.view.Gravity
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleOwner
import com.blankj.utilcode.util.ScreenUtils
import com.google.common.cache.CacheBuilder
import com.polaris.live.common.constant.PagConst
import com.polaris.live.common.manager.CoroutineScopeManager
import com.polaris.live.common.util.AppCodeUtils
import com.polaris.live.common.util.StructUtils
import com.polaris.live.common.util.removeIfBy24
import com.polaris.live.im.sfs.bean.live.ext.LiveEnterBean
import com.polaris.live.manager.DownFileManager
import com.polaris.live.resp.bean.EntryScreenBean
import com.polaris.live.resp.bean.LiveEffectBean
import com.polaris.live.resp.bean.LiveShowPagBean
import com.polaris.live.resp.bean.decoration.UserDecorationLiveCarExtBean
import com.polaris.live.utils.AudioPlayProxy
import com.polaris.live.utils.DecorationUtils
import com.polaris.live.utils.file.FileUtils
import com.polaris.live.utils.image.ImageUtils
import com.polaris.live.utils.viewscope.autoDisposeScope
import com.polaris.live.widget.LiveEntryScreenItemView
import com.polaris.live.widget.bubble.DownLoadFileUtil
import com.polaris.live.widget.pag.PagWidgetView
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.libpag.PAGFile
import org.libpag.PAGImage
import org.libpag.PAGScaleMode
import java.io.File
import java.util.concurrent.ConcurrentLinkedDeque
import java.util.concurrent.TimeUnit

/**
 * EntryScreenAnimationProxy
 * @author Created by 天晴 on 2024/1/29 14:20
 * @since 1.0.0
 **/
class EntryScreenAnimationProxy(
    private val mContext: Context,
    private val mEntryFloatingScreen: LinearLayout, //进场动效
    private val mEntryScreen: FrameLayout,  //进场座驾
    private val lifecycleOwner: LifecycleOwner,
) {

    init {
        lifecycleOwner.lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    cancel()
                }
            }
        })

        val params: ViewGroup.LayoutParams = mEntryFloatingScreen.layoutParams
        params.width = ViewGroup.LayoutParams.MATCH_PARENT
        mEntryFloatingScreen.layoutParams = params
    }

    //防止用户进进出出的显示 同一个用户5分钟不能展示
    private val mCache = CacheBuilder.newBuilder()
        .maximumSize(1000)
        .expireAfterWrite(5, TimeUnit.MINUTES)
        .build<Long, Long>()

    //下载job
    private val mJobList = StructUtils.newSaleSet<Job>()

    //下载队列 等待下载
    private val mDownList = StructUtils.newSaleSet<EntryScreenBean>()

    //等待队列  等待展示
    private val mWaitQueue = ConcurrentLinkedDeque<EntryScreenBean>()

    //展示中 的数据帮助类
    private val mEntryScreenHelper = EntryScreenHelper()

    /**
     * 添加一个进场动效 其他人的
     *
     * noScreen 没有进场动效
     */
    fun addEntryScreenOtherUser(livebean: LiveEnterBean, noScreen: () -> Unit) {
        if (livebean.joinEffectsNew.isNullOrEmpty()) {
            noScreen.invoke()
            return
        }
        if (lifecycleOwner.lifecycle.currentState == Lifecycle.State.DESTROYED ||
            (livebean.joinEffectsNew.isNullOrEmpty() && livebean.car.isNullOrEmpty() ||
                    mCache.getIfPresent(livebean.userId) != null)
        ) {
            return
        }
        val bean = EntryScreenBean(
            livebean,
            DecorationUtils.getDecorationLiveCar(livebean.car),
            DecorationUtils.getDecorationLiveEffect(livebean.joinEffectsNew),
            false,
            false,
            null,
            null,
            null,
            0
        )
        downPag(bean) {
            if (lifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED) {
                if (mDownList.any { da -> da === it }) {

                    mDownList.remove(it)
                    if (mEntryScreenHelper.isHaveShow()) {
                        mWaitQueue.add(it)
                    } else {
                        showAnimation(it)
                    }
                }
            }
        }

    }

    /**
     * 添加一个进场动效 自己
     *
     * noScreen 没有进场动效
     */
    fun addEntryScreenMe(livebean: LiveEnterBean) {
        if (lifecycleOwner.lifecycle.currentState == Lifecycle.State.DESTROYED ||
            (livebean.joinEffectsNew.isNullOrEmpty() && livebean.car.isNullOrEmpty())
        ) {
            return
        }
        val bean = EntryScreenBean(
            livebean,
            DecorationUtils.getDecorationLiveCar(livebean.car),
            DecorationUtils.getDecorationLiveEffect(livebean.joinEffectsNew),
            false,
            false,
            null,
            null,
            null,
            0
        )
        downPag(bean) {
            if (lifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED) {
                if (mDownList.any { da -> da === it }) {
                    mDownList.remove(it)
                    if (mEntryScreenHelper.isHaveShow()) {
                        mWaitQueue.addFirst(it)
                    } else {
                        showAnimation(it)
                    }
                }
            }
        }
    }

    /**
     * 移除一个进场动效
     */
    fun removeEntryScreen(userId: Long) {
        mDownList.removeIfBy24 { it.livebean.userId == userId }
        mWaitQueue.removeIfBy24 { it.livebean.userId == userId }
    }

    /**
     * 显示进场效果
     */
    private fun showAnimation(entryScreenBean: EntryScreenBean) {
        mEntryScreenHelper.showAnimation(entryScreenBean) {
            mWaitQueue.poll()?.let {
                showAnimation(it)
            }
        }
        entryScreenBean.carBean?.apply {
            userId = entryScreenBean.livebean.userId
            setUserDecorationLiveCarExtBean(this, entryScreenBean.livebean)
        }
        entryScreenBean.effectBean?.apply {
            addAnimation(
                LiveEffectBean(
                    entryScreenBean.livebean.userId,
                    entryScreenBean.livebean.nickname,
                    entryScreenBean.livebean.sex,
                    entryScreenBean.livebean.richs,
                    entryScreenBean.livebean.charms,
                    entryScreenBean.livebean.avatar,
                    entryScreenBean.livebean.avatarFrame,
                    url.endsWith(PagConst.PAG),
                    this,
                    entryScreenBean.carPagTime,
                    entryScreenBean.livebean.label
                )
            )
        }
        mCache.put(entryScreenBean.livebean.userId, System.currentTimeMillis())
    }

    /**
     * 添加进场座驾
     */
    private fun setUserDecorationLiveCarExtBean(udlce: UserDecorationLiveCarExtBean, livebean: LiveEnterBean) {
        val gra = when (udlce.gravity) {
            0 -> Gravity.TOP
            1 -> Gravity.CENTER
            2 -> Gravity.BOTTOM
            else -> Gravity.CENTER
        }
        LiveShowPagBean(
            udlce.userId,
            udlce.url,
            udlce.height / udlce.width.toDouble(),
            udlce.width,
            udlce.height,
            gra,
            soundEffect = udlce.soundEffect,
            livebean = livebean,
            carLayers = udlce.layers
        ).let {
            showCarPag(it)
        }
    }

    private fun showCarPag(liveShowPagBean: LiveShowPagBean) {
        val path = FileUtils.getFilePath(FileUtils.getPagFile(), liveShowPagBean.pagUrl)
        //如果文件里没有找到 就代表没有下载过 那么就去下载 然后播放下一个
        if (!File(path).exists()) {
            return
        }
        val width = ViewGroup.LayoutParams.MATCH_PARENT
        val height = (ScreenUtils.getScreenWidth() * liveShowPagBean.heightWidthRatio).toInt()
        val mGiftPag = PagWidgetView(mContext)
        mEntryScreenHelper.setGiftPag(mGiftPag)
        val layout = FrameLayout.LayoutParams(width, height)
        when (liveShowPagBean.gravity) {
            Gravity.CENTER -> {
                layout.gravity = Gravity.CENTER
            }

            Gravity.BOTTOM -> {
                layout.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
            }

            Gravity.TOP -> {
                layout.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
            }

            else -> {
                layout.gravity = Gravity.CENTER
            }
        }
        mGiftPag.layoutParams = layout
        mEntryScreen.removeAllViews()
        mEntryScreen.addView(mGiftPag)
        val pagFile = PAGFile.Load(path)
        val layers = liveShowPagBean.carLayers
        if (layers != null) {
            mGiftPag.autoDisposeScope.launch {
                val texts = layers.text
                if (texts?.isNotEmpty() == true) {
                    texts.forEach {
                        liveShowPagBean.livebean?.nickname?.let { name ->
                            val showText = if (name.length > 8) {
                                name.substring(0, 8)
                            } else {
                                name
                            }
                            val textData = pagFile.getTextData(it.index)
                            textData.text = showText
                            pagFile.replaceText(it.index, textData)
                        }

                    }
                }
                val images = layers.image
                if (images?.isNotEmpty() == true) {
                    images.forEach {
                        liveShowPagBean.livebean?.avatar?.let { avatar ->
                            val loadNetImageBitmap = ImageUtils.loadNetImageBitmap(mContext, avatar)
                            val pagImage = PAGImage.FromBitmap(loadNetImageBitmap)
                            pagFile.replaceImage(it.index, pagImage)

                        }

                    }
                }
            }
        }

        mGiftPag.apply {
            setRepeatCount(1)
            setScaleMode(PAGScaleMode.Zoom)
            composition = pagFile
            setOnPagEndCallback {
                AudioPlayProxy.instance().recycle()
                mEntryScreen.post {
                    mEntryScreen.removeAllViews()
                    mEntryScreenHelper.carAnimationEnd()
                }

            }
            startPag()
        }
        liveShowPagBean.soundEffect?.let {
            AudioPlayProxy.instance().init(it)
            AudioPlayProxy.instance().playSound()
        }

    }

    fun addAnimation(bean: LiveEffectBean) {
        val liveEntry = LiveEntryScreenItemView(mContext).apply {
            setData(bean)
            setOnEntryScreenAnimationEnd {
                mEntryFloatingScreen.post {
                    mEntryFloatingScreen.removeAllViews()
                    mEntryScreenHelper.effectAnimationEnd()
                }
            }
        }
        mEntryScreenHelper.setLiveEntryScreenItemView(liveEntry)
        mEntryFloatingScreen.addView(liveEntry)
        if (AppCodeUtils.isAR()) {
            liveEntry.startRtLAnimation()
        } else {
            liveEntry.startAnimation()
        }
    }

    private fun downPag(bean: EntryScreenBean, complete: (bean: EntryScreenBean) -> Unit) {
        mDownList.add(bean)
        val job = CoroutineScopeManager.ioScope.launch {
            try {
                /**
                 * 进场座驾下载
                 */
                val carDeferred = bean.carBean?.url?.takeIf { it.endsWith(PagConst.PAG) }?.let { url ->
                    async {
                        DownLoadFileUtil.getPagPath(url)
                    }
                }

                /**
                 * 进场座驾音效
                 */
                val carSoundDeferred = bean.carBean?.soundEffect?.let { url ->
                    async {
                        val audPath = FileUtils.getAudioFile()
                        val loadFileCallback = DownFileManager.loadFileCallback(audPath, url)
                        loadFileCallback?.second
                    }
                }

                /**
                 * 进场特效下载
                 */
                val effectDeferred = bean.effectBean?.url?.takeIf { it.endsWith(PagConst.PAG) }?.let { url ->
                    async {
                        DownLoadFileUtil.getPagPath(url)
                    }
                }
                val carPath = carDeferred?.await()
                val carSoundPath = carSoundDeferred?.await()
                val effectPath = effectDeferred?.await()

                bean.localCarPagPath = carPath
                bean.soundPath = carSoundPath //网络地址 未加前缀
                bean.localEffectPagPath = effectPath
                bean.isLoadCarResource = true
                bean.isLoadEffectResource = true

                PAGFile.Load(carPath).apply {
                    bean.carPagTime = duration() / 1000
                }
            } finally {
                mJobList.remove(coroutineContext[Job])
                withContext(CoroutineScopeManager.mainDispatcher) {
                    complete.invoke(bean)
                }

            }
        }
        mJobList.add(job)
    }

    fun getEndTime(): Long {
        return if (mWaitQueue.isNotEmpty()) {
            REMOVE_VIEW_SHORT
        } else {
            REMOVE_VIEW_LONG
        }
    }

    private fun cancel() {
        mDownList.clear()
        mJobList.forEach {
            it.cancel()
        }
        mJobList.clear()

        mWaitQueue.clear()

        mEntryScreenHelper.clear()

        AudioPlayProxy.instance().recycle()
    }

    fun reset() {
        cancel()
        mEntryFloatingScreen.removeAllViews()
        mEntryScreen.removeAllViews()
    }

    companion object {

        const val REMOVE_VIEW_LONG = 3000L      //移除延迟移除动画 3秒
        const val REMOVE_VIEW_SHORT = 1000L     //移除延迟移除动画 1秒
    }

}

class EntryScreenHelper {

    private var mBack: (() -> Unit)? = null
    private var mEntryScreenBean: EntryScreenBean? = null
    private var mGiftPag: PagWidgetView? = null
    private var mLiveEntryScreenItemView: LiveEntryScreenItemView? = null

    /**
     * 是否还在展示
     */
    fun isHaveShow(): Boolean {
        return mGiftPag != null || mLiveEntryScreenItemView != null
    }

    fun showAnimation(entryScreenBean: EntryScreenBean, back: () -> Unit) {
        mEntryScreenBean = entryScreenBean
        mBack = back
        mGiftPag = null
        mLiveEntryScreenItemView = null
    }

    fun setGiftPag(mGiftPag: PagWidgetView) {
        this.mGiftPag = mGiftPag
    }

    /**
     * 进场座驾动画结束
     */
    fun carAnimationEnd() {
        mGiftPag = null
        endAnim()
    }

    fun setLiveEntryScreenItemView(mLiveEntryScreenItemView: LiveEntryScreenItemView) {
        this.mLiveEntryScreenItemView = mLiveEntryScreenItemView
    }

    /**
     * 进场特效动画结束
     */
    fun effectAnimationEnd() {
        mLiveEntryScreenItemView = null
        endAnim()
    }

    private fun endAnim() {
        if (mGiftPag == null && mLiveEntryScreenItemView == null) {
            mBack?.invoke()
            mEntryScreenBean = null
            mBack = null
        }
    }

    fun clear() {
        mEntryScreenBean = null
        mBack = null
        mGiftPag?.stopPag()
        mGiftPag = null
        mLiveEntryScreenItemView?.cancelAnimation()
        mLiveEntryScreenItemView = null

    }
}